﻿#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <iostream>
#include <fstream>

using namespace cv;
using namespace std;

void AverageFiltering(string);
void GaussianFiltering(string);
void MeGiltering(string);
void Sobelimage(string);
void Cannyimage(string);

//1.以Lena为原始图像，通过OpenCV实现平均滤波，高斯滤波及中值滤波，比较滤波结果
//2.以Lena为原始图像，通过OpenCV使用Sobel及Canny算子检测，比较边缘检测结果。 
int main(void)
{
	string path = "E:\\AI\\AIsoftware\\opencv\\sources\\samples\\data\\lena.jpg";

	//对图像进行均值滤波
	AverageFiltering(path);

	//高斯滤波
	GaussianFiltering(path);

	//中值滤波
	MeGiltering(path);	

	//Sobel算子
	Sobelimage(path); 	

	//Canny算子
	Cannyimage(path);

	waitKey(0);
}

//对图像进行均值滤波
void AverageFiltering(string path)
{
	Mat srcImage = imread(path);
	if (!srcImage.data)
	{
		cout << "读取图片错误，请重新输入正确路径！\n";
		system("pause");
	}
	//imshow("Lena图像", srcImage);
  
	Mat afImage;
	blur(srcImage, afImage, Size(5, 5));
	imshow("Lena图像均值滤波", afImage);
	//waitKey(1000);	
}
//高斯滤波
void GaussianFiltering(string path)
{
	Mat srcImage = imread(path);
	if (!srcImage.data)
	{
		cout << "读取图片错误，请重新输入正确路径！\n";
		system("pause");
	}
	//imshow("Lena图像", srcImage);

	Mat gfImage;
	GaussianBlur(srcImage, gfImage, Size(5, 5), 0, 0);
	imshow("Lena图像高斯滤波", gfImage);
	//waitKey(1000);
}
//中值滤波
void MeGiltering(string path)
{
	Mat srcImage = imread(path);
	if (!srcImage.data)
	{
		cout << "读取图片错误，请重新输入正确路径！\n";
		system("pause");
	}
	//imshow("Lena图像", srcImage);

	Mat meImage;
	medianBlur(srcImage, meImage, 5);//中值滤波函数
	imshow("Lena图像中值滤波", meImage);
	//waitKey(1000);
}
//Sobel算子
void Sobelimage(string path)
{
	Mat src, src_gray,grad;
	int ksize = 3;   //ksize是Sobel算子的大小，必须为1、3、5、7
	int scale = 1;   //scale是缩放导数的比例常数，默认情况下没有伸缩系数
	int delta = 0;   //delta是一个可选的增量，默认情况下没有额外的值增加
	int ddepth = CV_16S; //即Sobel函数求完导数后会有负值，还有会大于255的值。而原图像是uint8，即8位无符号数，所以Sobel建立的图像位数不够，会有截断。因此要使用16位有符号的数据类型，即CV_16S。 

	src = imread(path);  //加载图片
	if (!src.data)
	{
		cout << "读取图片错误，请重新输入正确路径！\n";
		system("pause");
	}
	//cv.imread读取到的图片格式是BGR，采用BGR2GRAY
	cvtColor(src, src_gray, COLOR_BGR2GRAY);  //首先将图像转换成灰度值

	Mat grad_x, grad_y;      //x,y方向分别卷积
	Mat abs_grad_x, abs_grad_y;

	Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT); 	//Sobel算子，求X方向上导数
	convertScaleAbs(grad_x, abs_grad_x);  //将图片转回原来的uint8形式。否则将无法显示图像，而只是一副灰色的窗口。

	Sobel(src_gray, grad_y, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT);	   //Sobel算子，求Y方向上导数
	convertScaleAbs(grad_y, abs_grad_y);  //将图片转回原来的uint8形式显示

	addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);	   //加权重，融合X、Y方向 

	//imshow("Sobel X", abs_grad_x);     //x方向
	//imshow("Sobel Y", abs_grad_y);     //y方向
	imshow("Sobel Lena", grad);
	//waitKey(1000);
}
//Canny算子
void Cannyimage(string path)
{	
	Mat src, gray,src_gray,canny_output,draw; //原图，灰度图，canny_out函数输出图，最终图
	vector<vector<Point>> contours;//每个轮廓由一系列点组成
	vector<Vec4i> hierachy;        //定义了一个向量，其中每个元素保存了一个包含4个int整型的数组（Vec4i是Vec<int,4>的别名）
	double  threshold = 40;        //最低阈值，高阈值=低*3（课程PPT）
	int aptertureSize = 3;         //Soble算子的size，通常采用3*3，取值3

	src = imread(path);  //加载图片
	if (!src.data)
	{
		cout << "读取图片错误，请重新输入正确路径！\n";
		system("pause");
	}

	//灰度转换cvtColor	
	cvtColor(src, gray, COLOR_BGR2GRAY);
	//imshow("灰度图", gray);

	//采用高斯平滑GaussianBlur滤波
	GaussianBlur(gray, gray, Size(3, 3),0,0);
	//imshow("高斯平滑", gray);

	//Canny算子,提取图像边缘
	Canny(gray, canny_output, threshold, threshold*3, aptertureSize);
	//imshow("Canny",canny_output);

	//提取轮廓，用于提取图像的轮廓，输出为检测到的轮廓，每个轮廓被表示成一个point向量
	findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//绘制轮廓
	//CV_8UC1绘制的是灰度图,CV_8UC3 3通道彩色图
	draw = Mat::zeros(canny_output.size(), CV_8UC1);
	for (size_t i = 0; i < contours.size(); i++) 
	{
		drawContours(draw, contours, i, (200,200,200), 1, 8, hierachy, 0, Point(0, 0)); 
	}

	imshow("Canny算子", draw);	
	//waitKey(0);
}